#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

extern "C" {
//封装格式
#include "libavformat/avformat.h"
//解码
#include "libavcodec/avcodec.h"
//缩放
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
}

//用来将内存buffer的数据拷贝到buf
int read_packet(void *opaque, uint8_t *buf, int buf_size)
{

    //buf_size = FFMIN(buf_size, bd.size);

    //if (!buf_size)
        //return AVERROR_EOF;
    //printf("ptr:%p size:%zu bz%zu\n", bd.ptr, bd.size, buf_size);

    //[> copy internal buffer data to buf <]
    //memcpy(buf, bd.ptr, buf_size);
    //bd.ptr += buf_size;
    //bd.size -= buf_size;
    //return buf_size;
    //

    FILE* fp_r = (FILE*)opaque;
    //fprintf(stderr, "read pos = %d\n", ftell(fp_r));
    if(!feof(fp_r)){
        int len = fread(buf, 1, buf_size, fp_r);
        return len;
    }else{
        return -1;
    }
    //printf("%d = ", len);
    //static int tatol_len = 0;
    //tatol_len += len;
    //printf("[%d] = ", tatol_len);
}

// whence: SEEK_SET, SEEK_CUR, SEEK_END (like fseek) and AVSEEK_SIZE
int64_t seek_func(void* ptr, int64_t pos, int whence)
{
    // Quelle Abfragen:
    FILE* fp_s = (FILE*)ptr;

    //fprintf(stderr, "SEEK_SET = %d\n", SEEK_SET);
    //fprintf(stderr, "SEEK_CUR = %d\n", SEEK_CUR);
    //fprintf(stderr, "SEEK_END = %d\n", SEEK_END);
    //fprintf(stderr, "AVSEEK_SIZE = %d\n", AVSEEK_SIZE);
    fprintf(stderr, "pos = %d, whence = %d\n", pos, whence);

    if(AVSEEK_SIZE==whence)
    {
        return -1;
    }
    //fseek(fp_s, pos, whence);
    //return ftell(fp_s);

    // Return the new position:
    int slen = fseek(fp_s, pos, whence);
    int tlen = ftell(fp_s);

    fprintf(stderr, "slen = %d, tlen = %d\n", slen, tlen);
    return tlen;
}

void printf_ffmpeg_codecs()
{
    char *info = (char *)malloc(40000);
    memset(info, 0, 40000);

    av_register_all();

    AVCodec *c_temp = av_codec_next(NULL);

    while (c_temp != NULL)
    {
        if (c_temp->decode != NULL)
        {
            strcat(info, "[Decode]");
        }
        else
        {
            strcat(info, "[Encode]");
        }
        switch (c_temp->type)
        {
            case AVMEDIA_TYPE_VIDEO:
                //strcat(info, "[Video]");
                break;
            case AVMEDIA_TYPE_AUDIO:
                strcat(info, "[Audio]");
                break;
            default:
                //strcat(info, "[Other]");
                break;
        }
        sprintf(info, "%s %10s\n", info, c_temp->name);
        c_temp = c_temp->next;
    }
    puts(info);
    free(info);
}

int main(int argc, char* argv[])
{
    //输入进行解码的mp3文件
    char input_file[256] = "test.mp3";

    //输出解码后获取到的pcm音频数据文件
    char output_file[256] = "out.pcm";

    if(argc>1)
    {
        strcpy(input_file, argv[1]);
    }
    if(argc>2)
    {
        strcpy(output_file, argv[2]);
    }

    printf("input_file  = %s\n", input_file);
    printf("output_file = %s\n", output_file);

    //printf_ffmpeg_codecs();

start:

    FILE* fp = fopen(input_file, "rb");;
    if(!fp)
    {
        printf("open file fail\n");
        return 0;
    }

    //1.注册组件
    av_register_all();
    //封装格式上下文
    AVFormatContext *pFormatCtx = avformat_alloc_context();


    int bufSize = 4096;
    unsigned char *buf = (unsigned char*)av_malloc((size_t)(bufSize));

    //pFormatCtx->pb = avio_alloc_context(buf, bufSize, 0, fp, read_packet, NULL, seek_func);
    pFormatCtx->pb = avio_alloc_context(buf, bufSize, 0, fp, read_packet, NULL, NULL);
    pFormatCtx->flags |= AVFMT_FLAG_CUSTOM_IO;

    //2.打开输入音频文件
    //if (avformat_open_input(&pFormatCtx, input_file, NULL, NULL) != 0) {
    if (avformat_open_input(&pFormatCtx, NULL, NULL, NULL) != 0) {
        fprintf(stderr, "%s\n", "打开输入音频文件失败");
        return -1;
    }
    //3.获取音频信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        fprintf(stderr, "%s\n", "获取音频信息失败");
        return -1;
    }
    av_dump_format(pFormatCtx, 0, 0, 0);

    //音频解码，需要找到对应的AVStream所在的pFormatCtx->streams的索引位置
    int audio_stream_idx = -1;
    int i = 0;
    for (; i < pFormatCtx->nb_streams; i++) {
        //根据类型判断是否是音频流
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_stream_idx = i;
            //printf("idx = %d, codec_id = 0x%x\n", i, pFormatCtx->streams[audio_stream_idx]->codec->codec_id);
            break;
        }
    }
    //4.获取解码器
    //根据索引拿到对应的流,根据流拿到解码器上下文
    AVCodecContext *pCodeCtx = pFormatCtx->streams[audio_stream_idx]->codec;
    //再根据上下文拿到编解码id，通过该id拿到解码器
    //AVCodec *pCodec = avcodec_find_decoder_by_name("amrnb");
    //AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_AMR_NB);
    //AVCodec *pCodec = avcodec_find_decoder_by_name("libspeex");
    //AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_SPEEX);
    AVCodec *pCodec = avcodec_find_decoder(pCodeCtx->codec_id);
    printf("codec_id = 0x%x\n", pCodeCtx->codec_id);
    if (pCodec == NULL) {
        fprintf(stderr, "%s\n", "无法解码");
        return -2;
    }
    //5.打开解码器
    if (avcodec_open2(pCodeCtx, pCodec, NULL) < 0) {
        fprintf(stderr, "%s\n", "编码器无法打开");
        return -3;
    }
    //编码数据
    AVPacket *packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    //解压缩数据
    AVFrame *frame = av_frame_alloc();

    //frame->16bit 44100 PCM 统一音频采样格式与采样率
    SwrContext *swrCtx = swr_alloc();
    //重采样设置选项-----------------------------------------------------------start
    //输入的采样格式
    enum AVSampleFormat in_sample_fmt = pCodeCtx->sample_fmt;
    //输出的采样格式 16bit PCM
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    //输入的采样率
    int in_sample_rate = pCodeCtx->sample_rate;
    //输出的采样率
    int out_sample_rate = 16000;
    //输入的声道布局
    uint64_t in_ch_layout = pCodeCtx->channel_layout;
    //输出的声道布局
    uint64_t out_ch_layout = AV_CH_LAYOUT_MONO;

    swr_alloc_set_opts(swrCtx, out_ch_layout, out_sample_fmt, out_sample_rate, in_ch_layout, in_sample_fmt,
            in_sample_rate, 0, NULL);
    swr_init(swrCtx);
    //重采样设置选项-----------------------------------------------------------end
    //获取输出的声道个数
    int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
    //存储pcm数据
    uint8_t *out_buffer = (uint8_t *) av_malloc(2 * 44100);
    FILE *fp_pcm = fopen(output_file, "wb");
    int ret, got_frame, framecount = 0;
    //6.一帧一帧读取压缩的音频数据AVPacket
    printf("probesize = %d\n", pFormatCtx->probesize);
    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == audio_stream_idx) {
            //解码AVPacket->AVFrame
            ret = avcodec_decode_audio4(pCodeCtx, frame, &got_frame, packet);
            if (ret < 0) {
                fprintf(stderr, "%s\n", "解码完成");
            }

            //非0，正在解码
            if (got_frame) {
                //printf("解码%d帧", framecount++);
                int out_nb_samples = swr_convert(swrCtx, &out_buffer, 2 * 44100, (const uint8_t **)frame->data, frame->nb_samples);
                //获取sample的size
                int out_buffer_size = av_samples_get_buffer_size(NULL, out_channel_nb, out_nb_samples, out_sample_fmt, 1);
                //写入文件进行测试
                fwrite(out_buffer, 1, out_buffer_size, fp_pcm);
            }
        }
        av_packet_unref(packet);
    }
    fclose(fp);
    fclose(fp_pcm);
    av_frame_free(&frame);
    av_free(packet);
    av_free(out_buffer);
    swr_free(&swrCtx);
    //if (pFormatCtx->pb)
    //{
        //av_free(pFormatCtx->pb->buffer);
        //av_free(pFormatCtx->pb);
    //}
    avcodec_close(pCodeCtx);
    avformat_close_input(&pFormatCtx);

    printf("decode over\n");
    //goto start;
    return 0;
}

